<?php

/**
 * A PHP reader of Aborior's Simplex database
 *
 * PHP version 5
 *
 * Simplex is now a defunct database format, so this package only supports
 * reading of database to recover data.
 *
 * @category  Database
 * @package   MDB2_Driver_simplex
 * @author    Philippe Jausions <jausions@php.net>
 * @copyright 2008 by 11abacus / Philippe Jausions
 * @license   http://www.opensources.org/licenses/bsd-license.php BSD license
 * @version   CVS: $Id: $
 * @link      http://pear.11abacus.com/package/MDB2_Driver_simplex
 */

require_once 'MDB2/Driver/Reverse/Common.php';

/**
 * MDB2 Aborior's Simplex driver for the schema reverse engineering module
 *
 * @category  Database
 * @package   MDB2_Driver_simplex
 * @author    Philippe Jausions <jausions@php.net>
 * @copyright 2008 by 11abacus / Philippe Jausions
 * @license   http://www.opensources.org/licenses/bsd-license.php BSD license
 * @version   Revision: @package_version@
 * @link      http://pear.11abacus.com/package/MDB2_Driver_simplex
 */
class MDB2_Driver_Reverse_simplex extends MDB2_Driver_Reverse_Common
{
    /**
     * Gets the structure of a field into an array
     *
     * @param string $table_name name of table that should be used in method
     * @param string $field_name name of field that should be used in method
     *
     * @return array data array on success, a MDB2 error on failure
     */
    public function getTableFieldDefinition($table_name, $field_name)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }

        $result = $db->loadModule('Datatype', null, true);
        if (PEAR::isError($result)) {
            return $result;
        }

        list($schema, $table) = $this->splitTableSchema($table_name);

        try {
            $columns = $db->getConnection()->listFields($table);
        } catch (Exception $e) {
            return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
                                   $e->getMessage(), __FUNCTION__);
        }
        foreach ($columns as $column) {
            $column = array_change_key_case($column, CASE_LOWER);
            $column['name'] = $column['field'];
            unset($column['field']);
            if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
                if ($db->options['field_case'] == CASE_LOWER) {
                    $column['name'] = strtolower($column['name']);
                } else {
                    $column['name'] = strtoupper($column['name']);
                }
            } else {
                $column = array_change_key_case($column, $db->options['field_case']);
            }
            if ($field_name == $column['name']) {
                $mapped_datatype = $db->datatype->mapNativeDatatype($column);
                if (PEAR::isError($mapped_datatype)) {
                    return $mapped_datatype;
                }
                list($types, $length, $unsigned, $fixed) = $mapped_datatype;
                $notnull = false;
                $default = false;
                if (array_key_exists('default', $column)) {
                    $default = $column['default'];
                    if (is_null($default) && $notnull) {
                        $default = '';
                    }
                }

                $definition[0] = array(
                    'notnull' => $notnull,
                    'nativetype' => preg_replace('/^([a-z]+)[^a-z].*/i', '\\1', $column['type'])
                );
                if (!is_null($length)) {
                    $definition[0]['length'] = $length;
                }
                if (!is_null($unsigned)) {
                    $definition[0]['unsigned'] = $unsigned;
                }
                if (!is_null($fixed)) {
                    $definition[0]['fixed'] = $fixed;
                }
                if ($default !== false) {
                    $definition[0]['default'] = $default;
                }
                if ($column['type'] == 'AutoInteger (Seq)') {
                    $definition[0]['autoincrement'] = true;
                }
                foreach ($types as $key => $type) {
                    $definition[$key] = $definition[0];
                    if ($type == 'clob' || $type == 'blob') {
                        unset($definition[$key]['default']);
                    } elseif ($type == 'timestamp' && $notnull && empty($definition[$key]['default'])) {
                        $definition[$key]['default'] = '0000-00-00 00:00:00';
                    }
                    $definition[$key]['type'] = $type;
                    $definition[$key]['mdb2type'] = $type;
                }
                return $definition;
            }
        }

        return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
            'it was not specified an existing table column', __FUNCTION__);
    }

    /**
     * Gets the structure of an index into an array
     *
     * @param string $table_name name of table that should be used in method
     * @param string $index_name name of index that should be used in method
     * 
     * @return mixed data array on success, a MDB2 error on failure
     */
    public function getTableIndexDefinition($table_name, $index_name)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }

        list($schema, $table) = $this->splitTableSchema($table_name);

        $definition = array();
        try {
            $index = $db->getConnection()->getConstraint($table, $index_name);
    
            $colpos = 0;
            foreach ($index['fields'] as $field => $info) { 
                if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
                    if ($db->options['field_case'] == CASE_LOWER) {
                        $field = strtolower($field);
                    } else {
                        $field = strtoupper($field);
                    }
                }
                $definition['fields'][$field] = array(
                    'position' => ++$colpos,
                );
            }            
        } catch (Exception $e) {
            $err = $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
                $index_name . ' is not an existing table index', __FUNCTION__);
            return $err;
        }

        if (empty($definition)) {
            $err = $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
                $index_name . ' is not an existing table index', __FUNCTION__);
            return $err;
        }

        return $definition;
    }

    /**
     * Gets the structure of a constraint into an array
     *
     * @param string $table_name      name of table that should be used in method
     * @param string $constraint_name name of constraint that should be used in method
     * 
     * @return mixed data array on success, a MDB2 error on failure
     */
    public function getTableConstraintDefinition($table_name, $constraint_name)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }

        list($schema, $table) = $this->splitTableSchema($table_name);
        
        try {
            $definition = $db->getConnection()->getConstraint($table, 
                                                            $constraint_name);
        } catch (Exception $e) {
            $err = $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
                     $constraint_name . ' is not an existing table constraint', 
                     __FUNCTION__);
            return $err;
        }
        //echo __FILE__.' ['.__LINE__."] getTableConstraintDefinition($table, $constraint_name)\n";
        ///print_r($definition);

        return $definition;
    }

    /**
     * Returns information about a table or a result set
     *
     * @param object|string  $result  MDB2_result object from a query or a
     *                                 string containing the name of a table.
     *                                 While this also accepts a query result
     *                                 resource identifier, this behavior is
     *                                 deprecated.
     * @param int            $mode    a valid tableInfo mode
     *
     * @return array  an associative array with the information requested.
     *                 A MDB2_Error object on failure.
     *
     * @see MDB2_Driver_Common::setOption()
     */
    function tableInfo($result, $mode = null)
    {
        if (is_string($result)) {
            return parent::tableInfo($result, $mode);
        }

        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }

        $resource = MDB2::isResultCommon($result) ? $result->getResource() : $result;
        if (!is_resource($resource)) {
            return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
                'Could not generate result resource', __FUNCTION__);
        }

        if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
            if ($db->options['field_case'] == CASE_LOWER) {
                $case_func = 'strtolower';
            } else {
                $case_func = 'strtoupper';
            }
        } else {
            $case_func = 'strval';
        }

        $count = @mysql_num_fields($resource);
        $res   = array();
        if ($mode) {
            $res['num_fields'] = $count;
        }

        $db->loadModule('Datatype', null, true);
        for ($i = 0; $i < $count; $i++) {
            $res[$i] = array(
                'table' => $case_func(@mysql_field_table($resource, $i)),
                'name'  => $case_func(@mysql_field_name($resource, $i)),
                'type'  => @mysql_field_type($resource, $i),
                'length'   => @mysql_field_len($resource, $i),
                'flags' => @mysql_field_flags($resource, $i),
            );
            if ($res[$i]['type'] == 'string') {
                $res[$i]['type'] = 'char';
            } elseif ($res[$i]['type'] == 'unknown') {
                $res[$i]['type'] = 'decimal';
            }
            $mdb2type_info = $db->datatype->mapNativeDatatype($res[$i]);
            if (PEAR::isError($mdb2type_info)) {
                return $mdb2type_info;
            }
            $res[$i]['mdb2type'] = $mdb2type_info[0][0];
            if ($mode & MDB2_TABLEINFO_ORDER) {
                $res['order'][$res[$i]['name']] = $i;
            }
            if ($mode & MDB2_TABLEINFO_ORDERTABLE) {
                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
            }
        }

        return $res;
    }
}
?>